1   /*
2    * Copyright (C) 2010 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.cache;
18  
19  import com.google.caliper.AfterExperiment;
20  import com.google.caliper.BeforeExperiment;
21  import com.google.caliper.Benchmark;
22  import com.google.caliper.Param;
23  import com.google.common.primitives.Ints;
24  
25  import java.util.Random;
26  import java.util.concurrent.atomic.AtomicLong;
27  
28  /**
29   * Single-threaded benchmark for {@link LoadingCache}.
30   *
31   * @author Charles Fry
32   */
33  public class LoadingCacheSingleThreadBenchmark {
34    @Param({"1000", "2000"}) int maximumSize;
35    @Param("5000") int distinctKeys;
36    @Param("4") int segments;
37  
38    // 1 means uniform likelihood of keys; higher means some keys are more popular
39    // tweak this to control hit rate
40    @Param("2.5") double concentration;
41  
42    Random random = new Random();
43  
44    LoadingCache<Integer, Integer> cache;
45  
46    int max;
47  
48    static AtomicLong requests = new AtomicLong(0);
49    static AtomicLong misses = new AtomicLong(0);
50  
51    @BeforeExperiment void setUp() {
52      // random integers will be generated in this range, then raised to the
53      // power of (1/concentration) and floor()ed
54      max = Ints.checkedCast((long) Math.pow(distinctKeys, concentration));
55  
56      cache = CacheBuilder.newBuilder()
57          .concurrencyLevel(segments)
58          .maximumSize(maximumSize)
59          .build(
60              new CacheLoader<Integer, Integer>() {
61                @Override public Integer load(Integer from) {
62                  return (int) misses.incrementAndGet();
63                }
64              });
65  
66      // To start, fill up the cache.
67      // Each miss both increments the counter and causes the map to grow by one,
68      // so until evictions begin, the size of the map is the greatest return
69      // value seen so far
70      while (cache.getUnchecked(nextRandomKey()) < maximumSize) {}
71  
72      requests.set(0);
73      misses.set(0);
74    }
75  
76    @Benchmark int time(int reps) {
77      int dummy = 0;
78      for (int i = 0; i < reps; i++) {
79        dummy += cache.getUnchecked(nextRandomKey());
80      }
81      requests.addAndGet(reps);
82      return dummy;
83    }
84  
85    private int nextRandomKey() {
86      int a = random.nextInt(max);
87  
88      /*
89       * For example, if concentration=2.0, the following takes the square root of
90       * the uniformly-distributed random integer, then truncates any fractional
91       * part, so higher integers would appear (in this case linearly) more often
92       * than lower ones.
93       */
94      return (int) Math.pow(a, 1.0 / concentration);
95    }
96  
97    @AfterExperiment void tearDown() {
98      double req = requests.get();
99      double hit = req - misses.get();
100 
101     // Currently, this is going into /dev/null, but I'll fix that
102     System.out.println("hit rate: " + hit / req);
103   }
104 
105   // for proper distributions later:
106   // import JSci.maths.statistics.ProbabilityDistribution;
107   // int key = (int) dist.inverse(random.nextDouble());
108 }